#include "server.h"

#include <gd.h>

#define CODE_MAX_SIZE       8

struct _img_node
{/*{{{*/
    char code[CODE_MAX_SIZE+1];
    char *data;
    struct listp_link l;
};/*}}}*/

static struct
{/*{{{*/
    pthread_mutex_t lock;
    struct listp_link list;
    int num;
}/*}}}*/
img_pool;

// 定制
static int _gen_img(char *code, char **datap)
{/*{{{*/
    static const char CODE_CHAR[]="123456789ABDEFGHMNPQRTY";
    const int CODE_CHAR_SIZE=strlen(CODE_CHAR);
    const int CODE_SIZE=4;
    const int IMG_WIDTH=80;
    const int IMG_HEIGHT=30;
    double PI2=6.28318530717959;
    const double FONT_SIZE=21;
    unsigned int seed=0;
    gdImagePtr im;
    int i;
    int res;

    im=gdImageCreate(IMG_WIDTH, IMG_HEIGHT);
    if(!im)
    {
        errlog("%s: create image fail", __func__);
        return(-1);
    }

    int bg_color;
    bg_color=gdImageColorAllocate(im, random_int(240, 255, &seed),
            random_int(240, 255, &seed), random_int(240, 255, &seed));
    gdImageFill(im, 0, 0, bg_color);

    int font_color;
    font_color=gdImageColorAllocate(im, random_int(0, 50, &seed),
            random_int(0, 50, &seed), random_int(0, 50, &seed));
    int brect[8];
    char s[1+1];
    int angle;
    char *err;
    for(i=0; i<CODE_SIZE; ++i)
    {
        angle=random_int(-1, 1, &seed);
        if(angle > 0)
            angle=random_int(-45, -10, &seed);
        else
            angle=random_int(10, 45, &seed);
        s[0]=CODE_CHAR[random_int(0, CODE_CHAR_SIZE-1, &seed)];
        s[1]=ZERO;
        err=gdImageStringFT(im, brect, font_color, cnfg_info._font_.ttf,
                FONT_SIZE, PI2/720*angle, 13+i*13, 25, s);
        if(err)
        {
            errlog("%s: draw string fail(%s)", __func__, err);
            gdImageDestroy(im);
            return(-2); 
        }
        code[i]=s[0];
    }
    code[CODE_SIZE]=ZERO;

    // 背景噪音
    int rnd_color;
    rnd_color=gdImageColorAllocate(im, random_int(0, 200, &seed),
            random_int(100, 200, &seed), random_int(150, 200, &seed));
    for(i=0; i<300; ++i)
    {
        gdImageSetPixel(im, random_int(0, IMG_WIDTH-1, &seed),
                random_int(0, IMG_HEIGHT-1, &seed), rnd_color);
    }

    // 横线
    gdImageLine(im,
            5, random_int(2, IMG_HEIGHT-1-2, &seed),
            IMG_WIDTH/2, random_int(2, IMG_HEIGHT-1-2, &seed),
            font_color);
    gdImageLine(im,
            IMG_WIDTH/2, random_int(2, IMG_HEIGHT-1-2, &seed),
            IMG_WIDTH-5-1, random_int(2, IMG_HEIGHT-1-2, &seed),
            font_color);

    // 输出
    unsigned char *png;
    int png_size;
    png=(unsigned char *)gdImagePngPtr(im, &png_size);
    gdImageDestroy(im);
    if(!png)
    {
        errlog("%s: generate png fail", __func__);
        return(-3);
    }
    char *data;
    int data_size;
    ALLOC_MULTI_DOERR(data, char, png_size*2,
            errlog("%s: alloc png buffer", __func__);
            gdFree(png);
            return(-4));
    res=ossl_encode(OSSL_ENCODE_BASE64, png, png_size,
            data, &data_size);
    gdFree(png);
    if(res)
    {
        errlog("%s: encode png fail, res:%d", __func__, res);
        free(data);
        return(-5);
    }
    data[data_size]=ZERO;
    *datap=data;

    return(0);
}/*}}}*/

int get_img_from_pool(char *code, char **datap)
{/*{{{*/
    struct listp_link *p=NULL;

    pthread_mutex_lock(&img_pool.lock);     // lock
    if(img_pool.num > 0)
    {
        p=img_pool.list.next;
        listp_del(p);
        --img_pool.num;
    }
    pthread_mutex_unlock(&img_pool.lock);   // unlock

    if(p)
    {
        struct _img_node *node=listp_entry(p, struct _img_node, l);
        strcpy(code, node->code);
        *datap=node->data;
        free(node);
    }
    else
    {
        int res=_gen_img(code, datap);
        if(res)
        {
            errlog("%s: _gen_img fail, res:%d", __func__, res);
            return(-1);
        }
    }

    return(0);
}/*}}}*/

struct
{/*{{{*/
    unsigned long thrd_id;
}/*}}}*/
generator_ctl;

static void _generator()
{/*{{{*/
    const int CHECK_OPERATE_INFO_INTERVAL=1;
    unsigned int quit=IS_FALSE;
    time_t last_operate=0;
    const int IMG_POOL_UPPER=10000;
    const int IMG_POOL_LOWER=5000;
    const int GENERATE_NUM=500;
    const int GENERATE_INTERVAL_NORMAL=5;
    const int GENERATE_INTERVAL_URGENT=1;
    int generate_interval=GENERATE_INTERVAL_NORMAL;
    time_t last_generate=0;
    time_t now;
    int res;
    
    while(!quit)
    {
        now=curr_timestamp(NULL);

        if(now - last_generate > generate_interval)
        {/*{{{*/
            int img_num;

            pthread_mutex_lock(&img_pool.lock); //lock
            img_num=img_pool.num;
            pthread_mutex_unlock(&img_pool.lock); //unlock
            if(img_num < IMG_POOL_UPPER)
            {
                struct listp_link list;
                struct _img_node *node;
                char code[CODE_MAX_SIZE+1];
                char *data;
                int i;

                listp_init(&list);
                if(img_num > IMG_POOL_LOWER)
                    generate_interval=GENERATE_INTERVAL_NORMAL;
                else
                    generate_interval=GENERATE_INTERVAL_URGENT;
                for(i=0; i<GENERATE_NUM; ++i)
                {
                    res=_gen_img(code, &data);
                    if(res)
                    {
                        errlog("%s: _gen_img fail, res:%d",
                                __func__, res);
                        listp_free_memb(&list, struct _img_node, l, data);
                        break;
                    }
                    ALLOC_DOERR(node, struct _img_node,
                            errlog("%s: alloc img_node fail", __func__);
                            listp_free_memb(&list, struct _img_node, l,
                                data);
                            break);
                    strcpy(node->code, code);
                    node->data=data;
                    listp_add_tail(&node->l, &list);
                }
                if(i == GENERATE_NUM)
                {
                    pthread_mutex_lock(&img_pool.lock);
                    listp_append(&list, &img_pool.list);
                    img_pool.num+=GENERATE_NUM;
                    pthread_mutex_unlock(&img_pool.lock);
                    errlog("%s: generate %d img",
                            __func__, GENERATE_NUM);
                }
            }

            last_generate=now;
        }/*}}}*/

        if(now - last_operate > CHECK_OPERATE_INFO_INTERVAL)
        {/*{{{*/
            pthread_mutex_lock(&operate_info._listener_.lock); //lock
            quit=operate_info._listener_.quit;
            if(quit)
            {
                operate_info._listener_.state=THRD_SHUTDOWN;
            }
            pthread_mutex_unlock(&operate_info._listener_.lock); //unlock

            last_operate=now;
        }/*}}}*/

        sleep(1);
    }
    DEEP_SLEEP;
}/*}}}*/

int generator_start()
{/*{{{*/
    // operate_info
    pthread_mutex_init(&operate_info._generator_.lock, NULL);
    operate_info._generator_.quit=IS_FALSE;
    operate_info._generator_.state=THRD_RUNNING;

    // img_pool
    pthread_mutex_init(&img_pool.lock, NULL);
    listp_init(&img_pool.list);
    img_pool.num=0;

    // start
    if(thrd_create((THRD_FUNC_T)_generator, NULL,
                THRD_DETACH, &generator_ctl.thrd_id))
    {
        fprintf(stderr, "%s: generator thrd create fail\n", __func__);
        return(-1);
    }

    return(0);
}/*}}}*/
